package cn.edu.dgut.css.sai.security.oauth2.client.userinfo;

import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiSnsGetuserinfoBycodeRequest;
import com.dingtalk.api.response.OapiSnsGetuserinfoBycodeResponse;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taobao.api.ApiException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * 钉钉移动接入应用 OAuth2 登录时使用的 OAuth2UserService
 *
 * @author sai
 * @since 2.2
 */
final class SaiDingDingOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        return createDDOAuth2User(userRequest);
    }

    private OAuth2User createDDOAuth2User(OAuth2UserRequest userRequest) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> DDUserAttributes = mapper.readValue(mapper.writeValueAsBytes(getDDUserInfoByCode(userRequest).getUserInfo()), new TypeReference<>() {
            });
            Set<GrantedAuthority> authorities = new LinkedHashSet<>();
            authorities.add(new OAuth2UserAuthority(DDUserAttributes));
            return new DefaultOAuth2User(authorities, DDUserAttributes, "unionid");
        } catch (Exception ex) {
            OAuth2Error oauth2Error = new OAuth2Error("invalid_OAuth2User_response",
                    "构造 钉钉 OAuth2User 异常 : " + ex.getMessage(), null);
            throw new OAuth2AuthorizationException(oauth2Error, ex);
        }
    }

    private OapiSnsGetuserinfoBycodeResponse getDDUserInfoByCode(OAuth2UserRequest userRequest) {
        DingTalkClient client = new DefaultDingTalkClient(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri());
        OapiSnsGetuserinfoBycodeRequest req = new OapiSnsGetuserinfoBycodeRequest();
        req.setTmpAuthCode(userRequest.getAdditionalParameters().get("code").toString());
        try {
            return client.execute(req, userRequest.getClientRegistration().getClientId(), userRequest.getClientRegistration().getClientSecret());
        } catch (ApiException ex) {
            OAuth2Error oauth2Error = new OAuth2Error("invalid_userinfo_response",
                    "获取钉钉开放平台 getuserinfo_bycode Response 异常 : " + ex.getMessage(), null);
            throw new OAuth2AuthorizationException(oauth2Error, ex);
        }
    }
}
